summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/physical_core.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/physical_core.cpp')
-rw-r--r--src/core/hle/kernel/physical_core.cpp251
1 files changed, 205 insertions, 46 deletions
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 073039825..7fa8e2a85 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -1,62 +1,206 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/scope_exit.h"
#include "common/settings.h"
-#include "core/arm/dynarmic/arm_dynarmic_32.h"
-#include "core/arm/dynarmic/arm_dynarmic_64.h"
-#ifdef HAS_NCE
-#include "core/arm/nce/arm_nce.h"
-#endif
#include "core/core.h"
-#include "core/hle/kernel/k_scheduler.h"
+#include "core/debugger/debugger.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
+#include "core/hle/kernel/svc.h"
namespace Kernel {
-PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KScheduler& scheduler)
- : m_core_index{core_index}, m_system{system}, m_scheduler{scheduler} {
-#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
- // TODO(bunnei): Initialization relies on a core being available. We may later replace this with
- // an NCE interface or a 32-bit instance of Dynarmic. This should be abstracted out to a CPU
- // manager.
- auto& kernel = system.Kernel();
- m_arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
- system, kernel.IsMulticore(),
- reinterpret_cast<Core::DynarmicExclusiveMonitor&>(kernel.GetExclusiveMonitor()),
- m_core_index);
-#else
-#error Platform not supported yet.
-#endif
+PhysicalCore::PhysicalCore(KernelCore& kernel, std::size_t core_index)
+ : m_kernel{kernel}, m_core_index{core_index} {
+ m_is_single_core = !kernel.IsMulticore();
}
-
PhysicalCore::~PhysicalCore() = default;
-void PhysicalCore::Initialize(bool is_64_bit) {
-#if defined(HAS_NCE)
- if (Settings::IsNceEnabled()) {
- m_arm_interface = std::make_unique<Core::ARM_NCE>(m_system, m_system.Kernel().IsMulticore(),
- m_core_index);
+void PhysicalCore::RunThread(Kernel::KThread* thread) {
+ auto* process = thread->GetOwnerProcess();
+ auto& system = m_kernel.System();
+ auto* interface = process->GetArmInterface(m_core_index);
+
+ interface->Initialize();
+
+ const auto EnterContext = [&]() {
+ system.EnterCPUProfile();
+
+ // Lock the core context.
+ std::scoped_lock lk{m_guard};
+
+ // Check if we are already interrupted. If we are, we can just stop immediately.
+ if (m_is_interrupted) {
+ return false;
+ }
+
+ // Mark that we are running.
+ m_arm_interface = interface;
+ m_current_thread = thread;
+
+ // Acquire the lock on the thread parameters.
+ // This allows us to force synchronization with Interrupt.
+ interface->LockThread(thread);
+
+ return true;
+ };
+
+ const auto ExitContext = [&]() {
+ // Unlock the thread.
+ interface->UnlockThread(thread);
+
+ // Lock the core context.
+ std::scoped_lock lk{m_guard};
+
+ // On exit, we no longer are running.
+ m_arm_interface = nullptr;
+ m_current_thread = nullptr;
+
+ system.ExitCPUProfile();
+ };
+
+ while (true) {
+ // If the thread is scheduled for termination, exit.
+ if (thread->HasDpc() && thread->IsTerminationRequested()) {
+ thread->Exit();
+ }
+
+ // Notify the debugger and go to sleep if a step was performed
+ // and this thread has been scheduled again.
+ if (thread->GetStepState() == StepState::StepPerformed) {
+ system.GetDebugger().NotifyThreadStopped(thread);
+ thread->RequestSuspend(SuspendType::Debug);
+ return;
+ }
+
+ // Otherwise, run the thread.
+ Core::HaltReason hr{};
+ {
+ // If we were interrupted, exit immediately.
+ if (!EnterContext()) {
+ return;
+ }
+
+ if (thread->GetStepState() == StepState::StepPending) {
+ hr = interface->StepThread(thread);
+
+ if (True(hr & Core::HaltReason::StepThread)) {
+ thread->SetStepState(StepState::StepPerformed);
+ }
+ } else {
+ hr = interface->RunThread(thread);
+ }
+
+ ExitContext();
+ }
+
+ // Determine why we stopped.
+ const bool supervisor_call = True(hr & Core::HaltReason::SupervisorCall);
+ const bool prefetch_abort = True(hr & Core::HaltReason::PrefetchAbort);
+ const bool breakpoint = True(hr & Core::HaltReason::InstructionBreakpoint);
+ const bool data_abort = True(hr & Core::HaltReason::DataAbort);
+ const bool interrupt = True(hr & Core::HaltReason::BreakLoop);
+
+ // Since scheduling may occur here, we cannot use any cached
+ // state after returning from calls we make.
+
+ // Notify the debugger and go to sleep if a breakpoint was hit,
+ // or if the thread is unable to continue for any reason.
+ if (breakpoint || prefetch_abort) {
+ if (breakpoint) {
+ interface->RewindBreakpointInstruction();
+ }
+ if (system.DebuggerEnabled()) {
+ system.GetDebugger().NotifyThreadStopped(thread);
+ } else {
+ interface->LogBacktrace(process);
+ }
+ thread->RequestSuspend(SuspendType::Debug);
+ return;
+ }
+
+ // Notify the debugger and go to sleep on data abort.
+ if (data_abort) {
+ if (system.DebuggerEnabled()) {
+ system.GetDebugger().NotifyThreadWatchpoint(thread, *interface->HaltedWatchpoint());
+ }
+ thread->RequestSuspend(SuspendType::Debug);
+ return;
+ }
+
+ // Handle system calls.
+ if (supervisor_call) {
+ // Perform call.
+ Svc::Call(system, interface->GetSvcNumber());
+ return;
+ }
+
+ // Handle external interrupt sources.
+ if (interrupt || !m_is_single_core) {
+ return;
+ }
+ }
+}
+
+void PhysicalCore::LoadContext(const KThread* thread) {
+ auto* const process = thread->GetOwnerProcess();
+ if (!process) {
+ // Kernel threads do not run on emulated CPU cores.
return;
}
-#endif
-#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
- auto& kernel = m_system.Kernel();
- if (!is_64_bit) {
- // We already initialized a 64-bit core, replace with a 32-bit one.
- m_arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
- m_system, kernel.IsMulticore(),
- reinterpret_cast<Core::DynarmicExclusiveMonitor&>(kernel.GetExclusiveMonitor()),
- m_core_index);
+
+ auto* interface = process->GetArmInterface(m_core_index);
+ if (interface) {
+ interface->SetContext(thread->GetContext());
+ interface->SetTpidrroEl0(GetInteger(thread->GetTlsAddress()));
+ interface->SetWatchpointArray(&process->GetWatchpoints());
}
-#else
-#error Platform not supported yet.
-#endif
}
-void PhysicalCore::Run() {
- m_arm_interface->Run();
- m_arm_interface->ClearExclusiveState();
+void PhysicalCore::LoadSvcArguments(const KProcess& process, std::span<const uint64_t, 8> args) {
+ process.GetArmInterface(m_core_index)->SetSvcArguments(args);
+}
+
+void PhysicalCore::SaveContext(KThread* thread) const {
+ auto* const process = thread->GetOwnerProcess();
+ if (!process) {
+ // Kernel threads do not run on emulated CPU cores.
+ return;
+ }
+
+ auto* interface = process->GetArmInterface(m_core_index);
+ if (interface) {
+ interface->GetContext(thread->GetContext());
+ }
+}
+
+void PhysicalCore::SaveSvcArguments(KProcess& process, std::span<uint64_t, 8> args) const {
+ process.GetArmInterface(m_core_index)->GetSvcArguments(args);
+}
+
+void PhysicalCore::CloneFpuStatus(KThread* dst) const {
+ auto* process = dst->GetOwnerProcess();
+
+ Svc::ThreadContext ctx{};
+ process->GetArmInterface(m_core_index)->GetContext(ctx);
+
+ dst->GetContext().fpcr = ctx.fpcr;
+ dst->GetContext().fpsr = ctx.fpsr;
+}
+
+void PhysicalCore::LogBacktrace() {
+ auto* process = GetCurrentProcessPointer(m_kernel);
+ if (!process) {
+ return;
+ }
+
+ auto* interface = process->GetArmInterface(m_core_index);
+ if (interface) {
+ interface->LogBacktrace(process);
+ }
}
void PhysicalCore::Idle() {
@@ -69,16 +213,31 @@ bool PhysicalCore::IsInterrupted() const {
}
void PhysicalCore::Interrupt() {
- std::unique_lock lk{m_guard};
+ // Lock core context.
+ std::scoped_lock lk{m_guard};
+
+ // Load members.
+ auto* arm_interface = m_arm_interface;
+ auto* thread = m_current_thread;
+
+ // Add interrupt flag.
m_is_interrupted = true;
- m_arm_interface->SignalInterrupt();
- m_on_interrupt.notify_all();
+
+ // Interrupt ourselves.
+ m_on_interrupt.notify_one();
+
+ // If there is no thread running, we are done.
+ if (arm_interface == nullptr) {
+ return;
+ }
+
+ // Interrupt the CPU.
+ arm_interface->SignalInterrupt(thread);
}
void PhysicalCore::ClearInterrupt() {
- std::unique_lock lk{m_guard};
+ std::scoped_lock lk{m_guard};
m_is_interrupted = false;
- m_arm_interface->ClearInterrupt();
}
} // namespace Kernel